// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/android/java/java_type.h"

#include "base/logging.h"
#include "base/strings/string_util.h"

namespace content {
namespace {

    // Array component type names are similar to JNI type names, except for using
    // dots as namespace separators in class names.
    std::unique_ptr<JavaType> CreateFromArrayComponentTypeName(
        const std::string& type_name)
    {
        std::unique_ptr<JavaType> result(new JavaType());
        DCHECK(!type_name.empty());
        switch (type_name[0]) {
        case 'Z':
            result->type = JavaType::TypeBoolean;
            break;
        case 'B':
            result->type = JavaType::TypeByte;
            break;
        case 'C':
            result->type = JavaType::TypeChar;
            break;
        case 'S':
            result->type = JavaType::TypeShort;
            break;
        case 'I':
            result->type = JavaType::TypeInt;
            break;
        case 'J':
            result->type = JavaType::TypeLong;
            break;
        case 'F':
            result->type = JavaType::TypeFloat;
            break;
        case 'D':
            result->type = JavaType::TypeDouble;
            break;
        case '[':
            result->type = JavaType::TypeArray;
            result->inner_type = CreateFromArrayComponentTypeName(type_name.substr(1));
            break;
        case 'L':
            if (type_name == "Ljava.lang.String;") {
                result->type = JavaType::TypeString;
                result->class_jni_name = "java/lang/String";
            } else {
                result->type = JavaType::TypeObject;
                result->class_jni_name = type_name.substr(1, type_name.length() - 2);
                base::ReplaceSubstringsAfterOffset(
                    &result->class_jni_name, 0, ".", "/");
            }
            break;
        default:
            // Includes void (V).
            NOTREACHED();
        }
        return result;
    }

} // namespace

JavaType::JavaType()
{
}

JavaType::JavaType(const JavaType& other)
{
    *this = other;
}

JavaType::~JavaType()
{
}

JavaType& JavaType::operator=(const JavaType& other)
{
    if (this == &other)
        return *this;
    type = other.type;
    if (other.inner_type) {
        DCHECK_EQ(JavaType::TypeArray, type);
        inner_type.reset(new JavaType(*other.inner_type));
    } else {
        inner_type.reset();
    }
    class_jni_name = other.class_jni_name;
    return *this;
}

// static
JavaType JavaType::CreateFromBinaryName(const std::string& binary_name)
{
    JavaType result;
    DCHECK(!binary_name.empty());
    if (binary_name == "boolean") {
        result.type = JavaType::TypeBoolean;
    } else if (binary_name == "byte") {
        result.type = JavaType::TypeByte;
    } else if (binary_name == "char") {
        result.type = JavaType::TypeChar;
    } else if (binary_name == "short") {
        result.type = JavaType::TypeShort;
    } else if (binary_name == "int") {
        result.type = JavaType::TypeInt;
    } else if (binary_name == "long") {
        result.type = JavaType::TypeLong;
    } else if (binary_name == "float") {
        result.type = JavaType::TypeFloat;
    } else if (binary_name == "double") {
        result.type = JavaType::TypeDouble;
    } else if (binary_name == "void") {
        result.type = JavaType::TypeVoid;
    } else if (binary_name[0] == '[') {
        result.type = JavaType::TypeArray;
        result.inner_type = CreateFromArrayComponentTypeName(binary_name.substr(1));
    } else if (binary_name == "java.lang.String") {
        result.type = JavaType::TypeString;
        result.class_jni_name = "java/lang/String";
    } else {
        result.type = JavaType::TypeObject;
        result.class_jni_name = binary_name;
        base::ReplaceSubstringsAfterOffset(&result.class_jni_name, 0, ".", "/");
    }
    return result;
}

std::string JavaType::JNIName() const
{
    switch (type) {
    case JavaType::TypeBoolean:
        return "Z";
    case JavaType::TypeByte:
        return "B";
    case JavaType::TypeChar:
        return "C";
    case JavaType::TypeShort:
        return "S";
    case JavaType::TypeInt:
        return "I";
    case JavaType::TypeLong:
        return "J";
    case JavaType::TypeFloat:
        return "F";
    case JavaType::TypeDouble:
        return "D";
    case JavaType::TypeVoid:
        return "V";
    case JavaType::TypeArray:
        return "[" + inner_type->JNISignature();
    case JavaType::TypeString:
    case JavaType::TypeObject:
        return class_jni_name;
    }
    NOTREACHED();
    return std::string();
}

std::string JavaType::JNISignature() const
{
    if (type == JavaType::TypeString || type == JavaType::TypeObject)
        return "L" + JNIName() + ";";
    else
        return JNIName();
}

} // namespace content
